package com.example.stock.controller;

import com.example.stock.controller.dto.GBTopDTO;
import com.example.stock.model.GBSta;
import com.example.stock.model.WeekDetail;
import com.example.stock.service.GBStaService;
import com.example.stock.service.WeekDataService;
import com.example.stock.service.impl.GBStaServiceImpl;
import com.example.stock.util.DateUtil;
import com.example.stock.util.NumberUtil;
import com.example.stock.util.Result;
import com.example.stock.util.ResultGenerator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;

@CrossOrigin(origins = "*")
@RequestMapping("gb")
@RestController
public class GBStaController {

    @Autowired
    GBStaService gbStaService;

    @Autowired
    WeekDataService weekDataService;

    private List<GBTopDTO> buildTopData(List<GBSta> gbStaList, Function<GBTopDTO, Double> f) {

        String curDate = weekDataService.getWeekPctStatKeys().getFirst();

        List<GBTopDTO> list = new ArrayList<>();
        gbStaList.forEach(item -> {
            GBTopDTO gbTopDTO = new GBTopDTO();
            BeanUtils.copyProperties(item, gbTopDTO);

            Double basePrice = f.apply(gbTopDTO);

            WeekDetail currentWeekDetail = weekDataService.getCurrentWeekDetail(item.getStock());
            gbTopDTO.setCurrentPrice(currentWeekDetail.getClose());
            gbTopDTO.setCurPct(NumberUtil.calPct(gbTopDTO.getCurrentPrice(), basePrice));
            gbTopDTO.setCurDisDuration(DateUtil.calWeekDataDuration(curDate, gbTopDTO.getDate()));
            gbTopDTO.setCur30wPct(currentWeekDetail.getGb30wPct());
            gbTopDTO.setCur60wPct(currentWeekDetail.getDisPct());
            gbTopDTO.setIncNum(currentWeekDetail.getIncNum());
            gbTopDTO.setPctSum(currentWeekDetail.getPctSum());

            List<WeekDetail> rawData = weekDataService.getWeekRawData(gbTopDTO.getStock()).stream()
                            .filter(i -> i.getDate().compareTo(gbTopDTO.getDate()) >= 0)
                            //突破后最近的52周
                            .limit(52).toList();

            // min
            WeekDetail min = rawData.stream()
                    .min(Comparator.comparing(WeekDetail::getClose)).get();
            gbTopDTO.setMinDate(min.getDate());
            gbTopDTO.setMinPct(NumberUtil.calPct(min.getClose(), basePrice));

            // max
            WeekDetail max = rawData.stream()
                    .max(Comparator.comparing(WeekDetail::getClose)).get();
            gbTopDTO.setMaxDate(max.getDate());
            gbTopDTO.setMaxPct(NumberUtil.calPct(max.getClose(), basePrice));

            list.add(gbTopDTO);
        });

        return list;
    }

    @GetMapping("top/60w")
    Result gbTop60W() {
        List<GBSta> gbStaList = gbStaService.getDurGtList(GBStaServiceImpl.GB_60W_K, 60).stream().limit(550).toList();
        return ResultGenerator.genSuccessResult(buildTopData(gbStaList, GBSta::getGb60w));
    }

    @GetMapping("low/60w")
    Result gbLow60W() {
        List<GBSta> gbStaList = gbStaService.getGBStaList(GBStaServiceImpl.GB_60W_K)
                .stream().sorted(Comparator.comparing(GBSta::getDuration)).limit(350).toList();

        return ResultGenerator.genSuccessResult(buildTopData(gbStaList, GBSta::getGb60w));
    }

    @GetMapping("top/30w")
    Result gbTop30W() {
        List<GBSta> gbStaList = gbStaService.getGBStaList(GBStaServiceImpl.GB_30W_K)
                .stream().sorted(Comparator.comparing(GBSta::getDuration).reversed()).limit(200).toList();

        return ResultGenerator.genSuccessResult(buildTopData(gbStaList, GBSta::getGb30w));
    }

    @GetMapping("low/30w")
    Result gbLow30W() {
        List<GBSta> gbStaList = gbStaService.getGBStaList(GBStaServiceImpl.GB_30W_K)
                .stream().sorted(Comparator.comparing(GBSta::getDuration)).limit(200).toList();

        return ResultGenerator.genSuccessResult(buildTopData(gbStaList, GBSta::getGb30w));
    }

    @GetMapping("top/20w")
    Result gbTop20W() {
        List<GBSta> gbStaList = gbStaService.getGBStaList(GBStaServiceImpl.GB_20W_K)
                .stream().sorted(Comparator.comparing(GBSta::getDuration).reversed()).limit(100).toList();

        return ResultGenerator.genSuccessResult(buildTopData(gbStaList, GBSta::getGb30w));
    }

    @GetMapping("20w/{stock}")
    Result gb20W(@PathVariable String stock) {
        List<GBSta> gbStaList = gbStaService.getGBStaByStock(stock, GBStaServiceImpl.GB_20W_K);
        return ResultGenerator.genSuccessResult(gbStaList);
    }

    @GetMapping("30w/{stock}")
    Result gb30W(@PathVariable String stock) {
        List<GBSta> gbStaList = gbStaService.getGBStaByStock(stock, GBStaServiceImpl.GB_30W_K);
        return ResultGenerator.genSuccessResult(gbStaList);
    }

    @GetMapping("60w/{stock}")
    Result gb60w(@PathVariable String stock) {
        List<GBSta> gbStaList = gbStaService.getGBStaByStock(stock, GBStaServiceImpl.GB_60W_K);
        return ResultGenerator.genSuccessResult(gbStaList);
    }

    @GetMapping("15wup")
    Result gb15wUp(@RequestParam(required = false) String date) {
        if (date == null) {
            date = weekDataService.getWeekPctStatKeys().getFirst();
        }
        Map<String, Object> ret = gbUp(date, GBStaServiceImpl.GB_15W_K);
        return ResultGenerator.genSuccessResult(ret);
    }

    @GetMapping("20wup")
    Result gb20wUp(@RequestParam(required = false) String date) {
        if (date == null) {
            date = weekDataService.getWeekPctStatKeys().getFirst();
        }
        Map<String, Object> ret = gbUp(date, GBStaServiceImpl.GB_20W_K);
        return ResultGenerator.genSuccessResult(ret);
    }

    @GetMapping("30wup")
    Result gb30wUp(@RequestParam(required = false) String date) {
        if (date == null) {
            date = weekDataService.getWeekPctStatKeys().getFirst();
        }
        Map<String, Object> ret = gbUp(date, GBStaServiceImpl.GB_30W_K);
        return ResultGenerator.genSuccessResult(ret);
    }

    @GetMapping("60wup")
    Result gb60wUp(@RequestParam(required = false) String date) {
        if (date == null) {
            date = weekDataService.getWeekPctStatKeys().getFirst();
        }
        Map<String, Object> ret = gbUp(date, GBStaServiceImpl.GB_60W_K);
        return ResultGenerator.genSuccessResult(ret);
    }

    private Map<String, Object> gbUp(String date,String type) {
        Map<String, Object> ret = new HashMap<>();
        ret.put("date", weekDataService.getWeekPctStatKeys());
        ret.put("data", cal(i -> i.getDisPct() >= 0, date, type));
        ret.put("last", getLast(i -> i.getDisPct() >= 0,date, type));
        return ret;
    }

    private Map<String, WeekDetail> getLast(Predicate<GBSta> f, String date, String type) {
        List<GBSta> gbSta = gbStaService.getGBStaByDate(date, type);
        if (gbSta == null || gbSta.isEmpty()) {
            return null;
        }

        List<GBSta> list = gbSta.stream().filter(f).toList();

        Map<String, WeekDetail> ret = new HashMap<>();
        list.forEach(i -> {
            ret.put(
                    i.getStock(),
                    weekDataService.getCurrentWeekDetail(i.getStock())
            );
        });
        return ret;
    }

    private Map<String, List<GBSta>> cal(Predicate<GBSta> f, String date, String type) {

        List<GBSta> gb60WSta = gbStaService.getGBStaByDate(date, type);
        if (gb60WSta == null || gb60WSta.isEmpty()) {
            return null;
        }

        List<GBSta> list = gb60WSta.stream().filter(f).toList();

        Map<String, List<GBSta>> ret = new HashMap<>();
        list.forEach(i -> {
            // 前10条记录
            List<GBSta> list1 = gbStaService.getGBStaByStock(i.getStock(), type).stream()
                    .sorted(Comparator.comparing(GBSta::getDate).reversed())
                    .filter(item -> item.getDate().compareTo(date) <= 0)
                    .limit(10).toList();

            ret.put(i.getStock(),
                    list1.stream().sorted(Comparator.comparing(GBSta::getDate)).toList()
            );
        });
        return ret;
    }
}
